#include "iap.h"
#include "usb_com.h"
#include "main.h"
unsigned char *updateFlag = (unsigned char *)0x20004ff0;
//unsigned char updateFlag __attribute__((section(".ARM.__at_0x20004ff0")));
extern void FLASH_WriteNByte(uint32_t Addr, uint8_t *pBuff, uint32_t Len);
extern void FLASH_ReadNByte(uint32_t Addr, uint8_t *pBuff, uint32_t Len);
extern void FLASH_WriteOnePage(uint32_t Addr, uint8_t *pBuff, uint32_t Len);
extern uint8_t FLASH_ReadByte(uint32_t Addr);
unsigned short resendTick = 0;
unsigned char needJump = 0;

unsigned char updateTemp[8192];

unsigned long startAddr;
uint16_t OnePkgAll;
uint16_t OnePkgNow;
uint8_t OnePkgFlag = 0;
static uint16_t pkgNow ;
unsigned short OnePkgLen = 0;
static unsigned short tick = 0;
void update_loop()
{
    if (OnePkgLen == 1)
    {
        return;
    }
    tick++;
    if (tick >= 1000)
    {
        tick = 0;
        if (*updateFlag == 1)// 写入falsh的时候停止重发
        {
            update_send_pkg(pkgNow);
            resendTick++;
            if (resendTick >= 3)
            {
                *updateFlag = 0;
                needJump = 1;

            }
        }
    }
}


void update_send_pkg(int num)
{
    unsigned char reply[8];
    pkgNow = num;
    reply[0] = 0x55;
    reply[1] = 0xaa;
    reply[2] = 8;
    reply[3] = COMM_UPDATE;
    reply[4] = COMM_SUB_UPDATE_DATA_SEND;
    reply[5] = num >> 8;
    reply[6] = num & 0xff;
    reply[7] = chksum(reply, 7);
    usb_send(reply, 8);
}

void update_end(int code)
{
    *updateFlag = 0;
    unsigned char reply[7];
    reply[0] = 0x55;
    reply[1] = 0xaa;
    reply[2] = 7;
    reply[3] = COMM_UPDATE;
    reply[4] = COMM_SUB_UPDATE_STOP;
    reply[5] = code;
    reply[6] = chksum(reply, 6);
    usb_send(reply, 7);
}

void update_init()
{
    if (*updateFlag == 1)
    {
        update_send_pkg(0);
    }
    else
    {
        Jump_to_APP();
    }
}

void update_begin(unsigned char cmd, unsigned char subCmd, unsigned char *data)
{
    *updateFlag = 1;
    usb_reply_ok(cmd, subCmd, COM_OK);
}

void update_send(unsigned char cmd, unsigned char subCmd, unsigned char *data)
{
    #define PER_MASK 0xff
    uint16_t pkgAll ;

    pkgAll = ((data[5] << 8) | data[6]) ;
    pkgNow = ((data[7] << 8) | data[8]) ;
    uint32_t len = (data[9] << 8) | data[10];
    uint16_t updateIndex = (pkgNow & PER_MASK) << PKG_PER_BIT;
    memcpy(&updateTemp[updateIndex], &data[11], len); // 存入cache中
    if ((pkgNow & PER_MASK) == 0) // 如果是第一包就先保存地址
    {
        startAddr = FLASH_APP_ADDR + (pkgNow << PKG_PER_BIT);
    }
    if ((((pkgNow + 1) & PER_MASK) == 0) || pkgNow + 1 == pkgAll)
    {
        OnePkgLen = updateIndex + len;
        OnePkgAll = pkgAll;
        OnePkgNow = pkgNow;
        OnePkgFlag = 1;
    }
    else
    {
        update_send_pkg(pkgNow + 1);
    }
    tick = 0;
    resendTick = 0;
    // uint32_t addr = FLASH_APP_ADDR + (pkgNow << PKG_PER_BIT);
    // resendTick = 0;

    // if (memcmp((const void *)addr, &data[11], len) != 0)
    // {
    //     update_end(1);
    // }
    // else
    // {
    //     if (pkgNow + 1 == pkgAll)
    //     {
    //         update_end(0);
    //     }
    //     else
    //     {
    //         update_send_pkg(pkgNow + 1);
    //     }
    // }
    // for (int i = 0; i < len; i++)
    // {
    //     if (FLASH_ReadByte(addr + i) != data[11 + i])
    //     {

    //     }
    // }

}

void update_stop(unsigned char cmd, unsigned char subCmd, unsigned char *data)
{
    needJump = 1;
}

void update_version(unsigned char cmd, unsigned char subCmd, unsigned char *data)
{
    unsigned char reply[11];
    reply[0] = 0xaa;
    reply[1] = 0x55;
    reply[2] = 11;
    reply[3] = cmd;    reply[4] = subCmd;
    reply[5] = 1;
    reply[6] = 0;
    reply[7] = 0;
    reply[8] = 0;
    reply[9] = 0;
    reply[10] = chksum(reply, 10);
    usb_send(reply, 11);
}

void jump_deal()
{
    if (needJump)
    {
        Jump_to_APP();
    }
    if (OnePkgFlag == 1)
    {
        FLASH_WriteNByte(startAddr, updateTemp, OnePkgLen);
        if (memcmp((const void *)startAddr, updateTemp, OnePkgLen) != 0)
        {
            update_end(1);
        }
        else
        {
            if (OnePkgNow + 1 == OnePkgAll)
            {
                update_end(0);
            }
            else
            {
                update_send_pkg(OnePkgNow + 1);
            }
        }
        OnePkgFlag = 0;
    }
}

typedef void (*pfunction)(void);
void (*Jump_To_Application)(void);


// 复位所有外设
void Per_DeInit(void)
{
    /* 复位APB1上的外设 */
    RCC->APB1RSTR = 0xFFFFFFFFU;
    RCC->APB1RSTR = 0x00U;

    /* 复位APB2上的外设 */
    RCC->APB2RSTR = 0xFFFFFFFFU;
    RCC->APB2RSTR = 0x00U;
}
void USB_Disconnected(void)
{
    __HAL_RCC_USB_FORCE_RESET();
    HAL_Delay(200);
    __HAL_RCC_USB_RELEASE_RESET();

    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_Initure.Pin = GPIO_PIN_11 | GPIO_PIN_12;
    GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Initure.Pull = GPIO_PULLDOWN;
    GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
    HAL_Delay(300);
}
int Jump_to_APP()
{
    uint32_t JumpAddress;
	
    /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
    if (((*(volatile uint32_t *)FLASH_APP_ADDR) & 0x2FFE0000) == 0x20000000)
    {
        USB_Disconnected();
        // 关闭所有中断
        //__set_FAULTMASK(1);
        /* 关闭全局中断 */
        __disable_irq();

        /* 复位所有外设 */
        Per_DeInit();

        /* 设置所有时钟到默认状态，使用HSI时钟 */
        //RCC_DeInit();


        /* 关闭所有中断，清除所有中断挂起标志 */
        for (int i = 0; i < 8; i++)
        {
            NVIC->ICER[i] = 0xFFFFFFFF;
            NVIC->ICPR[i] = 0xFFFFFFFF;
        }

        /* 关闭滴答定时器，复位到默认值 */
        SysTick->CTRL = 0;
        SysTick->LOAD = 0;
        SysTick->VAL = 0;
        /* Jump to user application */
        JumpAddress = *(volatile uint32_t *)(FLASH_APP_ADDR + 4);
        Jump_To_Application = (pfunction) JumpAddress;
        /* Initialize user application's Stack Pointer */
        __set_MSP(*(volatile uint32_t *) FLASH_APP_ADDR);
        Jump_To_Application();
        return 0;
    }
    return -1;
}

